<template>
    <div class="container">
        
        <div class="content">
            <p>我是子应用首页： {{ time }}.</p>
            <div ref="contentText" class="content-text">
                <p>这是一个主程序的例子</p>
                <pre>
{
  name: 'user app', // app name registered
  entry: '//localhost:5174/user-app/',
  // entry: '/user-app/',
  container: '#userAppContainer',
  activeRule: '/userApp',
}
                </pre>

                <h2 id="section-1">主应用的 entry 对应的是 开发和部署后的哪个路径</h2>
                <p>entry 对应子应用访问的根路径， 所以子应用应该根据entry的配置进行调整，官网例子 <a href="javascript:void(0)" @click="toContent('#section-offical-demo')">后面说明</a></p>
                <ol>
                    <li>开发时应该设置<code>vite.config.ts</code> 中的开发服务的base, 与entry的路径部分（user-app）一致</li>
                    <li>部署时子应用的访问路径应该与entry一致</li>                    
                </ol>
                <h2 id="section-2">主应用的 activeRule</h2>
                <p><code>activeRule</code>代表激活子程序的路由，当主程序进入到<code>activeRule</code>这个路由时， 会先激活主程序的对应组件，然后再加载激活子程序，最后子程序会加载对应的路由组件</p>
                <p>所以主程序中的 <code>activeRule</code> 需是子程序路由的一部分， 但要与<code>entry</code>有区别</p>
                <h2 id="section-3">完整例子</h2>
                <h3>主程序</h3>
                <pre>
// main.ts
registerMicroApps(
  [
    {
      name: 'user app', // app name registered
      // entry: '//localhost:5174/user-app/',
      entry: '/user-app/',
      container: '#userAppContainer',
      activeRule: '/userApp',
    }
  ]
);

start();                    
                </pre>
                <pre>
// router/index.ts
const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path:'/',
      component: Index,
      meta:{
        title: '首页'
      },
      children: []
    },{
      // 如果没有这个路由，主程序会白屏，因为主程序不能识别 /userApp,
      // 这个例子中子程序要显示在 Index 组件内部，如果Index组件不在了，子程序的容器就会消失。
      // 会报Target container with #container not existed while xxx loading!错误
      path: '/userApp',
      component: Index,
      meta: {
        title: 'User子应用',
        isSubApp: true
      }
    }
  ]
})
                </pre>
                <h3>子程序</h3>
                <p>子程序用到了 <code>vite-plugin-qiankun</code> 这个插件，<a href="javascript:void(0)" @click="toContent('#plugin')">注意事项</a></p>
                <pre>
// vite.config.ts
export default defineConfig({
  plugins: [
    vue(),
    qiankun('user-app',{  // 修改0
      useDevMode: true,
    })
  ],
  base,                   // 修改1
  server: {
    cors: true            // 修改2
  },
  // ... other config options
})
                </pre>
                <pre>
// 程序入口文件
import { createApp, type App } from 'vue'
import { createPinia } from 'pinia'
import { renderWithQiankun, qiankunWindow } from 'vite-plugin-qiankun/dist/helper';
import MyApp from './App.vue'
import router from './router'
let root: App;
interface QiankunProps { container?: { querySelector: (selector: string) => Element } }

function render(props: QiankunProps) {
    const { container } = props;
    root = createApp(MyApp)
    root.use(createPinia())
    root.use(router)
    const c = container
        ? container.querySelector("#app")
        : document.getElementById("app")
    root.mount(c)
}

renderWithQiankun({
    mount(props) {
        console.log("vue3sub mount");
        render(props);
    },
    bootstrap() {
        console.log("bootstrap");
    },
    unmount(props: any) {
        console.log("vue3sub unmount");
        root.unmount();
    },
    update(props: any) {
        console.log("vue3sub update");
        console.log(props)
    },
});

if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
    render({});
}
                </pre>
                <pre>
// router
let base = "/"
if(!qiankunWindow.__POWERED_BY_QIANKUN__){
    // 只有在非 qiankun 子程序的情况下base 才生效
  base = import.meta.env.BASE_URL
}
const router = createRouter({
  history: createWebHistory(base),
  routes: [
    {
      path: "/userApp",
      component: Index
    },
  ]
})
                </pre>
                <h3>nginx</h3>
                <pre>
server {
    listen        80;
    server_name localhost;
    location / {
        root        html;
        index       index.html;
        try_files   $uri /index.html;
    }

    location /user-app {
        root        html;
        index       /user-app/index.html;
        try_files   $uri  /index.html;
    }
}
                </pre>
                <code>html</code> 目录如下
                <pre>
html
|
|- assets/
|- index.html 
|- user-app/
|----index.html
|----assets/
                </pre>
                <h2 id="section-4">开发子程序时</h2>
                <p>在vite.config.ts中增加如下代理</p>
<pre>
    proxy: {
      // 这个代理用于将  /user-app 以外的请求，指向基座工程，这样就可以在通过访问本工程的地址访问 基座工程
      // 基座工程中注册本应用的 entry 只写本应用的  base 就可以了
      // 这样做可以在开发时尽可能的靠近生产环境（基座工程与子工程放在同一 http 服务器下）
      // 基座工程最好不是一个其他开发人员正在使用的开发服务，hrm 不好使
      "^(?!/user-app)":{
        target: "http://localhost:5173/",
        changeOrigin: true
      }, 
</pre>
                <h2 id="section-5">插件说明</h2>
                <p>这个插件有一个 Bug , 当你的应用入口是二级目录时，无法正确加载子应用资源</p>
                <pre>
 const htmlPlugin: PluginFn = (qiankunName, microOption = {}) => {
  let isProduction: boolean

  const module2DynamicImport = ($: CheerioAPI, scriptTag: Element) => {
    if (!scriptTag) {
      return
    }
    const script$ = $(scriptTag)
    const moduleSrc = script$.attr('src')
    let appendBase = ''
    if (microOption.useDevMode && !isProduction) {
      // 这里  window.proxy.__INJECTED_PUBLIC_PATH_BY_QIANKUN__ 如果是  ip:port/children/user-app , 后面应该加 两个 .. ,但是这里固定只写了一个
      // 所以官网推荐的二级目录部署的方式就不能用了
      appendBase = '(window.proxy ? (window.proxy.__INJECTED_PUBLIC_PATH_BY_QIANKUN__ + \'..\') : \'\') + '
    }
    script$.removeAttr('src')
    script$.removeAttr('type')
    script$.html(`import(${appendBase}'${moduleSrc}')`)
    return script$
  }
                </pre>
                <h2 id="section-offical-demo">官方例子的解读</h2>
                
                <p>qiankun <a href="https://qiankun.umijs.org/zh/guide/tutorial#vue-%E5%BE%AE%E5%BA%94%E7%94%A8">官网例子</a>有如下几个关键配置</p>
                <ol>
                    <li>entry 设置的是子应用的根目录 <code>/</code> ，子应用开发服务器没有设置base, 所以也是 <code>/</code></li>
                    <li>activeRule 设置的是子应用的路由的base, 子应用也对应设置了base</li>
                </ol>
                <p>但是这个例子如果主子应用在同一IP端口，刷新页面就会找不到主应用， 只显示子应用</p>
                <p>所以官网有给了针对同一IP,端口的<a href="https://qiankun.umijs.org/zh/cookbook#%E5%9C%BA%E6%99%AF-1%E4%B8%BB%E5%BA%94%E7%94%A8%E5%92%8C%E5%BE%AE%E5%BA%94%E7%94%A8%E9%83%A8%E7%BD%B2%E5%88%B0%E5%90%8C%E4%B8%80%E4%B8%AA%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%90%8C%E4%B8%80%E4%B8%AA-ip-%E5%92%8C%E7%AB%AF%E5%8F%A3">例子</a></p>
                <p>这个例子注意以下几点</p>
                <ol>
                    <li>子应用的访问目录 entry 不能和 activeRule 相同</li>
                    <li>activeRule 一定是 单独访问子应用时，路由的一部分</li>
                </ol>
                <h2 id="section-6">TODO</h2>
                <h2 id="section-7">TODO</h2>
            </div>
        </div>
        <ul class="nav">
            <li @click="toContent('#section-1')">主应用的 entry</li>
            <li @click="toContent('#section-2')">主应用的 activeRule</li>
            <li @click="toContent('#section-3')">完整例子</li>
            <li @click="toContent('#section-4')">开发子程序时</li>
            <li @click="toContent('#section-5')">插件说明</li>
            <li @click="toContent('#section-offical-demo')">官方例子的解读</li>
            <li @click="toContent('#section-6')">应用之间怎么通信</li>
            <li @click="toContent('#section-7')">共通的包怎么共享</li>

        </ul>
    </div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import dayjs from 'dayjs'
const time = ref('')
setInterval(() => time.value = dayjs().format(), 1000)

const contentText = ref<HTMLElement|null>(null)
const toContent = (id: string) =>{
    if(!contentText || !contentText.value){
        return
    }
    contentText.value.querySelector(id)?.scrollIntoView()
}
</script>
<style scoped lang="scss" >
.container {
    position: relative;
    height: 100%;
    width: 100%;
    display: grid;
    grid-template-columns:  1fr 200px;
    grid-column-gap: 30px;
    ul.nav {
        list-style: none;
        overflow: auto;
        li {
            margin-bottom: 5px;
            text-decoration-line: underline;
            text-indent: 24px;
            color: rgb(131, 129, 223);
            cursor: pointer;
            word-wrap: break-word;
            word-break: break-all;
        }
        li:hover{
            color: rgb(70, 69, 151);
            font-weight: bolder;
        }
    }
    div.content{
        position: relative;
        overflow: auto;
        
        div.content-text p{
            margin-bottom: 0.1em;
            text-indent: 24px;
        }

    }
    code{
        color: rebeccapurple;
        font-weight: bolder;
    }
    pre{
        background-color: rgb(180, 238, 206);
        padding: 10px;
        border-radius: 4px;
        line-height: 1em;
        margin: 6px 15px;
        max-height: 200px;
        -webkit-box-shadow: 0px 0px 10px 0px rgba(50, 50, 50, 0.9);
        -moz-box-shadow:    0px 0px 10px 0px rgba(50, 50, 50, 0.9);
        box-shadow:         0px 0px 10px 0px rgba(50, 50, 50, 0.9);
    }
}
</style>